package com.hisense.ovcloud.asop.service;

import com.hisense.ovcloud.asop.clients.BasicDataClient;
import com.hisense.ovcloud.asop.config.Constants;
import com.hisense.ovcloud.asop.dao.CustomDevicePairDao;
import com.hisense.ovcloud.asop.dao.CustomerDeviceDao;
import com.hisense.ovcloud.asop.dao.WifiUpgradeResultDao;
import com.hisense.ovcloud.asop.dto.clients.CustomerDto;
import com.hisense.ovcloud.asop.dto.db.BindDevicesInfoDto;
import com.hisense.ovcloud.asop.dto.req.PublicInfoReq;
import com.hisense.ovcloud.asop.dto.resp.ASOPBaseResp;
import io.micrometer.common.util.StringUtils;
import io.smallrye.mutiny.tuples.Tuple2;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.web.reactive.function.server.ServerRequest;
import reactor.core.publisher.Mono;

import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

@Service
@Slf4j
public class PublicInfoService {
    private final CustomerDeviceDao customerDeviceDao;

    private final BasicDataClient basicDataClient;

    private final WifiUpgradeResultDao wifiUpgradeResultDao;

    private final CustomDevicePairDao customDevicePairDao;

    public PublicInfoService(CustomerDeviceDao customerDeviceDao, BasicDataClient basicDataClient, WifiUpgradeResultDao wifiUpgradeResultDao, CustomDevicePairDao customDevicePairDao) {
        this.customerDeviceDao = customerDeviceDao;
        this.basicDataClient = basicDataClient;
        this.wifiUpgradeResultDao = wifiUpgradeResultDao;
        this.customDevicePairDao = customDevicePairDao;
    }

    private int searchTenantId(List<CustomerDto> cdl, String customerId) {
        AtomicInteger tenantId = new AtomicInteger();
        cdl.forEach(x -> {
            if (x.getCustomerId().equals(customerId)) {
                tenantId.set(x.getTenantId());
            }
        });
        return tenantId.get();
    }

    public Mono<ASOPBaseResp> fetchBindDevicesInfo(ServerRequest req) {
        return basicDataClient.verifyToken(req)
                .zipWhen(checkTokenResp -> {
                    if (checkTokenResp.isExist()) {
                        try {
                            return req.bodyToMono(PublicInfoReq.class)
                                    .flatMap(r -> {
                                        if (StringUtils.isNotEmpty(r.getEmail())) {
                                            return basicDataClient.queryCustomerInfo(r.getEmail())
                                                    .zipWhen(cl -> {
                                                        if (cl.isEmpty()) {
                                                            return Mono.just(ASOPBaseResp.builder().data(new ArrayList<>()).build());
                                                        } else {
                                                            return Mono.zip(
                                                                    Mono.just(cl.stream().map(CustomerDto::getCustomerId).toList())
                                                                            .zipWhen(customerIdList -> {
                                                                                        List<BindDevicesInfoDto> rawList = customerDeviceDao.fetchBindDevicesInfo(customerIdList, r);
                                                                                        rawList.forEach(x -> x.setTenantId(searchTenantId(cl, x.getCustomerid())));
                                                                                        return Mono.just(rawList);
                                                                                    },
                                                                                    (x, y) -> y),
                                                                    Mono.just(cl.stream().map(CustomerDto::getCustomerId).toList())
                                                                            .zipWhen(customerIdList -> Mono.just(customerDeviceDao.fetchBindDevicesInfoTotal(customerIdList, r)), (x, y) -> y)
                                                            ).zipWhen(tuple -> Mono.just(ASOPBaseResp.builder().data(tuple.getT1()).pageNum(r.getPageNum()).pageSize(r.getPageSize()).total(tuple.getT2()).build()), (x, y) -> y);
                                                        }
                                                    }, (x, y) -> y);
                                        } else if (StringUtils.isNotEmpty(r.getDeviceId()) || StringUtils.isNotEmpty(r.getPuid()) || StringUtils.isNotEmpty(r.getWifiId())) {
                                            return Mono.zip(
                                                    Mono.just(customerDeviceDao.fetchBindDevicesInfo(new ArrayList<>(), r)),
                                                    Mono.just(customerDeviceDao.fetchBindDevicesInfoTotal(new ArrayList<>(), r))
                                            ).zipWhen(tuple -> Mono.just(ASOPBaseResp.builder().data(tuple.getT1()).pageNum(r.getPageNum()).pageSize(r.getPageSize()).total(tuple.getT2()).build()), (x, y) -> y);
                                        } else {
                                            return Mono.just(ASOPBaseResp.builder().data(new ArrayList<>()).build());
                                        }
                                    });
                        } catch (Exception e) {
                            log.error("fetchBindDevicesInfo error: {}", e.getMessage());
                            return Mono.just(ASOPBaseResp.builder().data(new ArrayList<>()).resultCode(Constants.FAILED).errorMsg(e.getMessage()).build());
                        }
                    } else {
                        return Mono.just(ASOPBaseResp.builder()
                                .data(new ArrayList<>())
                                .resultCode(Constants.INVALID_TOKEN)
                                .errorMsg(Constants.ERRORS.BAD_TOKEN)
                                .build());
                    }
                }, (x, y) -> y);

    }


    public Mono<ASOPBaseResp> fetchWifiUpgradeResults(ServerRequest req) {
        return basicDataClient.verifyToken(req)
                .zipWhen(checkTokenResp -> {
                    if (checkTokenResp.isExist()) {
                        try {
                            return req.bodyToMono(PublicInfoReq.class).flatMap(r -> {
                                if (StringUtils.isNotEmpty(r.getEmail())) {
                                    return basicDataClient.queryCustomerInfo(r.getEmail())
                                            .zipWhen(cl -> {
                                                if (cl.isEmpty()) {
                                                    return Mono.just(ASOPBaseResp.builder().data(new ArrayList<>()).build());
                                                } else {
                                                    return Mono.just(cl.stream().map(CustomerDto::getCustomerId).toList())
                                                            .zipWhen(customerIdList -> Mono.just(customerDeviceDao.fetchWifiIdViaCustomIds(customerIdList)), (x, y) -> y)
                                                            .zipWhen(wifiIdList -> Mono.zip(
                                                                            Mono.just(wifiUpgradeResultDao.fetchWifiUpgradeResults(wifiIdList, r)),
                                                                            Mono.just(wifiUpgradeResultDao.fetchWifiUpgradeResultsTotal(wifiIdList, r))
                                                                    ).zipWhen(tuple -> Mono.just(
                                                                            ASOPBaseResp.builder().data(tuple.getT1()).pageNum(r.getPageNum()).pageSize(r.getPageSize()).total(tuple.getT2()).build()
                                                                    ), (x, y) -> y)
                                                                    , (x, y) -> y);
                                                }
                                            }, (x, y) -> y);
                                } else if (StringUtils.isNotEmpty(r.getDeviceId()) || StringUtils.isNotEmpty(r.getPuid()) || StringUtils.isNotEmpty(r.getWifiId())) {
                                    return Mono.zip(
                                            Mono.just(wifiUpgradeResultDao.fetchWifiUpgradeResults(new ArrayList<>(), r)),
                                            Mono.just(wifiUpgradeResultDao.fetchWifiUpgradeResultsTotal(new ArrayList<>(), r))
                                    ).zipWhen(tuple -> Mono.just(
                                            ASOPBaseResp.builder().data(tuple.getT1()).pageNum(r.getPageNum()).pageSize(r.getPageSize()).total(tuple.getT2()).build()
                                    ), (x, y) -> y);
                                } else {
                                    return Mono.just(ASOPBaseResp.builder().data(new ArrayList<>()).build());
                                }
                            });
                        } catch (Exception e) {
                            log.error("fetchWifiUpgradeResults error: {}", e.getMessage());
                            return Mono.just(ASOPBaseResp.builder().data(new ArrayList<>()).resultCode(Constants.FAILED).errorMsg(e.getMessage()).build());
                        }
                    } else {
                        return Mono.just(ASOPBaseResp.builder()
                                .data(new ArrayList<>())
                                .resultCode(Constants.INVALID_TOKEN)
                                .errorMsg(Constants.ERRORS.BAD_TOKEN)
                                .build());
                    }
                }, (x, y) -> y);
    }

    public Mono<ASOPBaseResp> fetchBindHistory(ServerRequest req) {
        return basicDataClient.verifyToken(req)
                .zipWhen(checkTokenResp -> {
                    if (checkTokenResp.isExist()) {
                        try {
                            return req.bodyToMono(PublicInfoReq.class).flatMap(r -> {
                                if (StringUtils.isNotEmpty(r.getEmail())) {
                                    return basicDataClient.queryCustomerInfo(r.getEmail())
                                            .zipWhen(cl -> {
                                                if (cl.isEmpty()) {
                                                    return Mono.just(ASOPBaseResp.builder().data(new ArrayList<>()).build());
                                                } else {
                                                    return Mono.just(cl.stream().map(CustomerDto::getCustomerId).toList())
                                                            .zipWhen(customerIdList -> Mono.just(customerDeviceDao.fetchWifiIdViaCustomIds(customerIdList)), Tuple2::of)
                                                            .zipWhen(tp -> Mono.zip(
                                                                    Mono.just(customDevicePairDao.fetchBindHistory(tp.getItem2(), tp.getItem1(), r)),
                                                                    Mono.just(customDevicePairDao.fetchBindHistoryTotal(tp.getItem2(), tp.getItem1(), r))
                                                            ).zipWhen(tuple -> {
                                                                if (tuple.getT2() > 0) {
                                                                    return Mono.just(ASOPBaseResp.builder()
                                                                            .data(tuple.getT1())
                                                                            .pageNum(r.getPageNum())
                                                                            .pageSize(r.getPageSize())
                                                                            .total(tuple.getT2())
                                                                            .build());
                                                                } else {
                                                                    return Mono.just(ASOPBaseResp.builder().data(new ArrayList<>()).build());
                                                                }
                                                            }, (x, y) -> y)
                                                                    , (x, y) -> y);
                                                }
                                            }, (x, y) -> y);
                                } else if (StringUtils.isNotEmpty(r.getDeviceId()) || StringUtils.isNotEmpty(r.getPuid()) || StringUtils.isNotEmpty(r.getWifiId())) {
                                    return Mono.zip(
                                            Mono.just(customDevicePairDao.fetchBindHistory(new ArrayList<>(), new ArrayList<>(), r)),
                                            Mono.just(customDevicePairDao.fetchBindHistoryTotal(new ArrayList<>(), new ArrayList<>(), r))
                                    ).zipWhen(tuple -> {
                                        if (tuple.getT2() > 0) {
                                            return Mono.just(ASOPBaseResp.builder()
                                                    .data(tuple.getT1())
                                                    .pageNum(r.getPageNum())
                                                    .pageSize(r.getPageSize())
                                                    .total(tuple.getT2())
                                                    .build());
                                        } else {
                                            return Mono.just(ASOPBaseResp.builder().data(new ArrayList<>()).build());
                                        }

                                    }, (x, y) -> y);
                                } else {
                                    return Mono.just(ASOPBaseResp.builder().data(new ArrayList<>()).build());
                                }
                            });
                        } catch (Exception e) {
                            log.error("fetchBindHistory error: {}", e.getMessage());
                            return Mono.just(ASOPBaseResp.builder().data(new ArrayList<>()).resultCode(Constants.FAILED).errorMsg(e.getMessage()).build());
                        }
                    } else {
                        return Mono.just(ASOPBaseResp.builder()
                                .data(new ArrayList<>())
                                .resultCode(Constants.INVALID_TOKEN)
                                .errorMsg(Constants.ERRORS.BAD_TOKEN)
                                .build());
                    }
                }, (x, y) -> y);
    }

    private long calculateTimestampForPair(String pairTime, String zoneId) {
        if (StringUtils.isNotEmpty(zoneId) && zoneId.contains("/")) {
            return LocalDateTime.parse("2023-12-28 02:19:00", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")).atZone(ZoneId.of(zoneId)).toEpochSecond() * 1000;
        } else {
            return LocalDateTime.parse("2023-12-28 02:19:00", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")).atZone(ZoneId.of("UTC")).toEpochSecond() * 1000;
        }
    }
}
